home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 08 / Neat Stuff.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  11KB  |  348 lines

  1. #include "Neat Stuff.h"
  2. #include "Script.h"
  3.  
  4. #define NIL 0L
  5.  
  6. const short        kData = 1;
  7. const short        kResource = 2;
  8.  
  9. const short        kGetDirectoryDLOGID = 6042;
  10. const short        kDirectorySelectButton = 10;
  11.  
  12. Ptr                gBufferPtr = NIL;
  13. long            gAmountInBuffer = 0;
  14. const long        kCopyBufferSize = 20 * 1024;
  15.  
  16. /*******************************************************************************
  17.  
  18.     CopyAFile
  19.  
  20.     Queries the user for a single file and a destination for a copy of that
  21.     file. The file is then copied to that new location. The copy is performed
  22.     in small chunks that will handle any file, no matter how large. First, the
  23.     data fork is copied. If that goes OK, the resource fork is copied. If
  24.     there is no error, the directory information for that file (including the
  25.     Finder information) is transferred. However, any Finder comments
  26.     associated with the file are not transferred as they are kept in the
  27.     Finder’s private data file.
  28.  
  29. *******************************************************************************/
  30. OSErr CopyAFile()
  31. {
  32.     OSErr        err;
  33.     FSSpec        source;
  34.     FSSpec        destination;
  35.  
  36.     //
  37.     // Get the name of a file to copy, and a destination for the copy.
  38.     //
  39.  
  40.     if (!GetAFile(&source))
  41.         return userCanceledErr;
  42.  
  43.     if (!GetADirectory(&destination))
  44.         return userCanceledErr;
  45.  
  46.     //
  47.     // If the destination is the same as the source, do nothing.
  48.     //
  49.  
  50.     if ((source.vRefNum == destination.vRefNum) && (source.parID == destination.parID))
  51.         return noErr;
  52.  
  53.     BlockMove(&source.name, &destination.name, source.name[0] + 1);
  54.  
  55.     //
  56.     // Get a little buffer for reading and writing. Abort if there
  57.     // are any errors.
  58.     //
  59.  
  60.     gBufferPtr = NewPtr(kCopyBufferSize);
  61.     if (MemError() != noErr) return MemError();
  62.     if (gBufferPtr == NIL) return memFullErr;
  63.  
  64.     //
  65.     // Copy the data and resource forks. Transfer the directory information.
  66.     //
  67.  
  68.     err = CopyFork(&source, &destination, kData);
  69.     if (err == noErr)
  70.         err = CopyFork(&source, &destination, kResource);
  71.     if (err == noErr)
  72.         err = TransferCatInfo(&source, &destination);
  73.     if (err != noErr)
  74.         err = FSpDelete(&destination);
  75.  
  76.     DisposePtr(gBufferPtr);
  77.     return err;
  78. }
  79.  
  80.  
  81. /*******************************************************************************
  82.  
  83.     CopyFork
  84.  
  85.     Given a source file and a destination specification, copy the fork
  86.     designated by “whichFork.” Open the source and destination files, creating
  87.     the destination if necessary. Set the size of the destination fork to the
  88.     size of the source fork (we can quickly catch any “Out of disk space”
  89.     errors this way). Then copy the fork in small chunks: read a little, then
  90.     write a little until the whole fork is copied. Close both files when we’re
  91.     done. Leave it up to the caller to delete the destination on any errors.
  92.  
  93. *******************************************************************************/
  94. OSErr    CopyFork(FSSpec* source, FSSpec* destination, short whichFork)
  95. {
  96.     OSErr        err;
  97.     Boolean        done;
  98.     short        sourceRefNum;
  99.     short        destinationRefNum;
  100.  
  101.     err = OpenFiles(source, destination, &sourceRefNum, &destinationRefNum, whichFork);
  102.     if (err != noErr) return err;
  103.  
  104.     err = SetDestinationFileSize(sourceRefNum, destinationRefNum);
  105.     if (err != noErr) return err;
  106.  
  107.     done = FALSE;
  108.     do {
  109.         gAmountInBuffer = kCopyBufferSize;
  110.         err = FSRead(sourceRefNum, &gAmountInBuffer, gBufferPtr);
  111.         if (err == eofErr) {
  112.             err = noErr;
  113.             done = TRUE;
  114.         }
  115.         if (err == noErr)
  116.             err = FSWrite(destinationRefNum, &gAmountInBuffer, gBufferPtr);
  117.     } while (!done && (err == noErr));
  118.     FSClose(sourceRefNum);
  119.     FSClose(destinationRefNum);
  120.  
  121.     return err;
  122. }
  123.  
  124.  
  125. /*******************************************************************************
  126.  
  127.     OpenFiles
  128.  
  129.     Given FSSpecs for two files, open the designated fork for both files. The
  130.     source is assumed to exist. If the destination does not exist, it is
  131.     created. The refNums for both open files are returned. If there are any
  132.     errors, the refNums are not valid, and both files are closed.
  133.  
  134.     The code for opening the resource fork of a file is almost identical to
  135.     the code for opening a data fork. Therefore, at the start of the function,
  136.     we set a procedure pointer to either a routine that opens the data fork or
  137.     a routine that opens a resource fork, whichever is appropriate. Whatever
  138.     routine is pointed to is called in the heart of the logic of the rest of
  139.     the function as needed.
  140.  
  141. *******************************************************************************/
  142. OSErr    OpenFiles(FSSpec* source, FSSpec* destination,
  143.                     short* sourceRefNum, short* destRefNum, short whichFork)
  144. {
  145.     typedef OSErr (*OpenProcPtr) (const FSSpec *spec, char permission, short *refNum);
  146.  
  147.     OpenProcPtr        openProc;
  148.     OSErr            err;
  149.  
  150.     if (whichFork == kData) {
  151.         openProc = DoOpenDF;
  152.     } else {
  153.         openProc = DoOpenRF;
  154.     }
  155.  
  156.     err = openProc(source, fsRdPerm, sourceRefNum);
  157.     if (err != noErr)
  158.         return err;
  159.  
  160.     err = openProc(destination, fsRdWrPerm, destRefNum);
  161.     if (err == fnfErr) {
  162.         err = FSpCreate(destination, 'Fox ', 'Trot', smSystemScript);
  163.         if (err == noErr)
  164.             err = openProc(destination, fsRdWrPerm, destRefNum);
  165.     }
  166.  
  167.     if (err != noErr)
  168.         FSClose(*sourceRefNum);
  169.  
  170.     return err;
  171. }
  172.  
  173. //
  174. // Wrappers for FSpOpenDF and FSpOpenRF. We genericize the above routine by
  175. // using a pointer to a function that opens either a data fork or a resource
  176. // fork. However, FSpOpenDF and FSpOpenRF implemented with inline instructions,
  177. // so there is no function that we can point to. Neither can we use
  178. // GetTrapAddress to get the address of these routines; they share a single
  179. // trap, and, hence, jump to the same address. The system knows which function
  180. // you really want by a “selector” value that is pushed on the stack by the
  181. // the inline instructions.
  182. //
  183.  
  184. OSErr    DoOpenDF(const FSSpec *spec, char permission, short *refNum)
  185. {
  186.     return FSpOpenDF( (FSSpec*) spec, permission, refNum);
  187. }
  188.  
  189. OSErr    DoOpenRF(const FSSpec *spec, char permission, short *refNum)
  190. {
  191.     return FSpOpenRF( (FSSpec*) spec, permission, refNum);
  192. }
  193.  
  194.  
  195. /*******************************************************************************
  196.  
  197.     TransferCatInfo
  198.  
  199.     Transfer the directory information of the source file to the destination
  200.     file. Call PBGetCatInfo on the source file. If that succeeds, call
  201.     PBSetCatInfo on the destination.
  202.  
  203. *******************************************************************************/
  204. OSErr    TransferCatInfo(FSSpec* source, FSSpec* destination)
  205. {
  206.     CInfoPBRec    pb;
  207.     OSErr        err;
  208.  
  209.     pb.hFileInfo.ioVRefNum = source->vRefNum;
  210.     pb.hFileInfo.ioNamePtr = source->name;
  211.     pb.hFileInfo.ioFDirIndex = 0;
  212.     pb.hFileInfo.ioDirID = source->parID;
  213.     err = PBGetCatInfoSync(&pb);
  214.  
  215.     if (err == noErr) {
  216.         pb.hFileInfo.ioVRefNum = destination->vRefNum;
  217.         pb.hFileInfo.ioNamePtr = destination->name;
  218.         pb.hFileInfo.ioFDirIndex = 0;
  219.         pb.hFileInfo.ioDirID = destination->parID;
  220.         err = PBSetCatInfoSync(&pb);
  221.     }
  222.  
  223.     return err;
  224. }
  225.  
  226.  
  227. /*******************************************************************************
  228.  
  229.     SetDestinationFileSize
  230.  
  231.     Given the refNums to two open file forks, set the size of the destination
  232.     to be the same as the source. Call GetEOF on the source file. If that
  233.     succeeds, we use the result in a call to SetEOF for the destination file.
  234.  
  235. *******************************************************************************/
  236. OSErr    SetDestinationFileSize(short sourceRefNum, short destinationRefNum)
  237. {
  238.     OSErr    err;
  239.     long    forkSize;
  240.  
  241.     err = GetEOF(sourceRefNum, &forkSize);
  242.     if (err == noErr)
  243.         err = SetEOF(destinationRefNum, forkSize);
  244.  
  245.     return err;
  246. }
  247.  
  248.  
  249. /*******************************************************************************
  250.  
  251.     GetAFile
  252.  
  253.     Get the FSSpec of a file to open. Call StandardGetFile to allow the user
  254.     to select any file. We pass “-1” for the numTypes parameter to tell
  255.     Standard File to display all files. Copy the FSSpec of the selected file
  256.     to the FSSpec supplied by the caller. Return a Boolean indicating whether
  257.     or not the user pressed Cancel. If the user _did_ press Cancel, the FSSpec
  258.     is not valid.
  259.  
  260. *******************************************************************************/
  261. Boolean    GetAFile(FSSpec* file)
  262. {
  263.     SFTypeList            types;                // dummy - not used
  264.     StandardFileReply    reply;
  265.  
  266.     StandardGetFile(NIL,                    // FileFilterProcPtr    fileFilter
  267.                     -1,                        // short                numTypes
  268.                     types,                    // SFTypeList            typeList
  269.                     &reply);                // StandardFileReply    *reply
  270.  
  271.     if (reply.sfGood)
  272.         BlockMove(&reply.sfFile, file, sizeof(FSSpec));
  273.  
  274.     return reply.sfGood;
  275. }
  276.  
  277.  
  278. /*******************************************************************************
  279.  
  280.     GetADirectory
  281.  
  282.     Return a specification for the directory where the file should be copied.
  283.     Call CustomGetFile to allow the user to select a directory. If the user
  284.     selects a directory, the vRefNum and dirID are copied into the caller’s
  285.     FSSpec; the “name” field is not used. A Boolean is returned indicating
  286.     whether or not the user selected Cancel. If the user _did_ press Cancel,
  287.     the FSSpec is not valid.
  288.  
  289.     The interface for selecting a directory is similar to the standard GetFile
  290.     dialog box. Two items have been added: a “Select” button and a line of
  291.     help text. The idea is that the user moves into the destination directory
  292.     and then presses the Select button.
  293.  
  294.     To implement the “Select a directory” dialog box, we need to use a custom
  295.     file filter and a custom dialog hook. The file filter is used to display
  296.     folders only, and to filter out any files. The dialog hook is used to
  297.     handle clicks on the Select button. If the user clicks on that button, we
  298.     dismiss the dialog by faking Standard File into thinking the Cancel button
  299.     was clicked. However, we use the “yourDataPtr” parameter in CustomGetFile
  300.     to pass a pointer to a Boolean. We use this Boolean to keep track of
  301.     whether Cancel was really clicked on, or if we are just faking a click on
  302.     Cancel. When CustomGetFile returns, we look at the Boolean to decide
  303.     whether a directory was selected or not.
  304.  
  305. *******************************************************************************/
  306. Boolean    GetADirectory(FSSpec* directory)
  307. {
  308.     SFTypeList            types;
  309.     StandardFileReply    reply;
  310.     Boolean                reallyPressedSelect = FALSE;
  311.     Point                where = {-1, -1};    // center it on the main screen
  312.  
  313.     CustomGetFile(    (FileFilterYDProcPtr) GetDirFileFilter,
  314.                     -1,                        // short                numTypes,
  315.                     types,                    // SFTypeList            typeList,
  316.                     &reply,                    // StandardFileReply    *reply,
  317.                     kGetDirectoryDLOGID,    // short                dlgID,
  318.                     where,                    // Point                where,
  319.                     (DlgHookYDProcPtr) GetDirDlgHook,
  320.                     NIL,                    // ModalFilterYDProcPtr    filterProc,
  321.                     NIL,                    // short                *activeList,
  322.                     NIL,                    // ActivateYDProcPtr    activateProc,
  323.                     (void*) &reallyPressedSelect);    // void            *yourDataPtr);
  324.  
  325.     if (reallyPressedSelect)
  326.         BlockMove(&reply.sfFile, directory, sizeof(FSSpec));
  327.  
  328.     return reallyPressedSelect;
  329. }
  330.  
  331. pascal Boolean GetDirFileFilter(ParmBlkPtr PB, StandardFileReply *replyPtr)
  332. {
  333.     if ((PB->fileParam.ioFlAttrib & ioDirMask) != 0)
  334.         return FALSE;                        // It’s a directory; show it
  335.     else
  336.         return TRUE;                        // It’s a file; don’t show it
  337. }
  338.  
  339. pascal short GetDirDlgHook(short item, DialogPtr theDialog,
  340.                             Boolean *reallyPressedSelect)
  341. {
  342.     if (item == kDirectorySelectButton) {
  343.         *reallyPressedSelect = TRUE;        // Keep track of what really happened
  344.         item = sfItemCancelButton;            // Fake a click on the Cancel button
  345.     }
  346.     return item;
  347. }
  348.